{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "9a8bceb3-95bd-4496-bb9e-57655136e070",
      "metadata": {},
      "source": [
        "# How to convert Runnables to Tools\n",
        "\n",
        "```{=mdx}\n",
        "\n",
        ":::info Prerequisites\n",
        "\n",
        "This guide assumes familiarity with the following concepts:\n",
        "\n",
        "- [Runnables](/docs/concepts/runnables)\n",
        "- [Tools](/docs/concepts/tools)\n",
        "- [Agents](https://langchain-ai.github.io/langgraphjs/tutorials/quickstart/)\n",
        "\n",
        ":::\n",
        "\n",
        "```\n",
        "\n",
        "For convenience, `Runnables` that accept a string or object input can be converted to tools using the [`asTool`](https://api.js.langchain.com/classes/langchain_core.runnables.Runnable.html#asTool) method, which allows for the specification of names, descriptions, and additional schema information for arguments.\n",
        "\n",
        "Here we will demonstrate how to use this method to convert a LangChain `Runnable` into a tool that can be used by agents, chains, or chat models.\n",
        "\n",
        "```{=mdx}\n",
        ":::caution Compatibility\n",
        "\n",
        "This functionality requires `@langchain/core>=0.2.16`. Please see here for a [guide on upgrading](/docs/how_to/installation/#installing-integration-packages).\n",
        "\n",
        ":::\n",
        "```\n",
        "\n",
        "## `asTool`\n",
        "\n",
        "Tools have some additional requirements over general Runnables:\n",
        "\n",
        "- Their inputs are constrained to be serializable, specifically strings and objects;\n",
        "- They contain names and descriptions indicating how and when they should be used;\n",
        "- They contain a detailed `schema` property for their arguments. That is, while a tool (as a `Runnable`) might accept a single object input, the specific keys and type information needed to populate an object should be specified in the `schema` field.\n",
        "\n",
        "The `asTool()` method therefore requires this additional information to create a tool from a runnable. Here's a basic example:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "b2cc4231-64a3-4733-a284-932dcbf2fcc3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Explanation of when to use the tool.\n"
          ]
        }
      ],
      "source": [
        "import { RunnableLambda } from \"@langchain/core/runnables\";\n",
        "import { z } from \"zod\";\n",
        "\n",
        "const schema = z.object({\n",
        "  a: z.number(),\n",
        "  b: z.array(z.number()),\n",
        "});\n",
        "\n",
        "\n",
        "const runnable = RunnableLambda.from((input: z.infer<typeof schema>) => {\n",
        "  return input.a * Math.max(...input.b);\n",
        "});\n",
        "\n",
        "const asTool = runnable.asTool({\n",
        "  name: \"My tool\",\n",
        "  description: \"Explanation of when to use the tool.\",\n",
        "  schema,\n",
        "});\n",
        "\n",
        "asTool.description"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "54ae7384-a03d-4fa4-8cdf-9604a4bc39ee",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "6\n"
          ]
        }
      ],
      "source": [
        "await asTool.invoke({ a: 3, b: [1, 2] })"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7c474d85-4e01-4fae-9bba-0c6c8c26475c",
      "metadata": {},
      "source": [
        "Runnables that take string inputs are also supported:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "c475282a-58d6-4c2b-af7d-99b73b7d8a13",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Adds letters to a string.\n"
          ]
        }
      ],
      "source": [
        "const firstRunnable = RunnableLambda.from<string, string>((input) => {\n",
        "  return input + \"a\";\n",
        "})\n",
        "\n",
        "const secondRunnable = RunnableLambda.from<string, string>((input) => {\n",
        "  return input + \"z\";\n",
        "})\n",
        "\n",
        "const runnable = firstRunnable.pipe(secondRunnable)\n",
        "const asTool = runnable.asTool({\n",
        "  name: \"append_letters\",\n",
        "  description: \"Adds letters to a string.\",\n",
        "  schema: z.string(),\n",
        "})\n",
        "\n",
        "asTool.description;"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "ad6d8d96-3a87-40bd-a2ac-44a8acde0a8e",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "baz\n"
          ]
        }
      ],
      "source": [
        "await asTool.invoke(\"b\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "89fdb3a7-d228-48f0-8f73-262af4febb58",
      "metadata": {},
      "source": [
        "## In an agents\n",
        "\n",
        "Below we will incorporate LangChain Runnables as tools in an [agent](/docs/concepts/agents) application. We will demonstrate with:\n",
        "\n",
        "- a document [retriever](/docs/concepts/retrievers);\n",
        "- a simple [RAG](/docs/tutorials/rag/) chain, allowing an agent to delegate relevant queries to it.\n",
        "\n",
        "We first instantiate a chat model that supports [tool calling](/docs/how_to/tool_calling/):\n",
        "\n",
        "```{=mdx}\n",
        "<ChatModelTabs\n",
        "  customVarName=\"llm\"\n",
        "/>\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e8a2038a-d762-4196-b5e3-fdb89c11e71d",
      "metadata": {},
      "source": [
        "Following the [RAG tutorial](/docs/tutorials/rag/), let's first construct a retriever:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "23d2a47e-6712-4294-81c8-2c1d76b4bb81",
      "metadata": {},
      "outputs": [],
      "source": [
        "import { ChatOpenAI } from \"@langchain/openai\";\n",
        "\n",
        "const llm = new ChatOpenAI({ model: \"gpt-3.5-turbo-0125\", temperature: 0 })\n",
        "\n",
        "import { Document } from \"@langchain/core/documents\"\n",
        "import { MemoryVectorStore } from \"langchain/vectorstores/memory\";\n",
        "import { OpenAIEmbeddings } from \"@langchain/openai\";\n",
        "\n",
        "const documents = [\n",
        "  new Document({\n",
        "    pageContent: \"Dogs are great companions, known for their loyalty and friendliness.\",\n",
        "  }),\n",
        "  new Document({\n",
        "    pageContent: \"Cats are independent pets that often enjoy their own space.\",\n",
        "  }),\n",
        "]\n",
        "\n",
        "const vectorstore = await MemoryVectorStore.fromDocuments(\n",
        "  documents, new OpenAIEmbeddings(),\n",
        ");\n",
        "\n",
        "const retriever = vectorstore.asRetriever({\n",
        "  k: 1,\n",
        "  searchType: \"similarity\",\n",
        "});"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9ba737ac-43a2-4a6f-b855-5bd0305017f1",
      "metadata": {},
      "source": [
        "We next create a pre-built [LangGraph agent](/docs/how_to/migrate_agent/) and provide it with the tool:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "c939cf2a-60e9-4afd-8b47-84d76ccb13f5",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "AGENT: AIMessage {\n",
            "  \"id\": \"chatcmpl-9m9RIN1GQVeXcrVdp0lNBTcZFVHb9\",\n",
            "  \"content\": \"\",\n",
            "  \"additional_kwargs\": {\n",
            "    \"tool_calls\": [\n",
            "      {\n",
            "        \"id\": \"call_n30LPDbegmytrj5GdUxZt9xn\",\n",
            "        \"type\": \"function\",\n",
            "        \"function\": \"[Object]\"\n",
            "      }\n",
            "    ]\n",
            "  },\n",
            "  \"response_metadata\": {\n",
            "    \"tokenUsage\": {\n",
            "      \"completionTokens\": 17,\n",
            "      \"promptTokens\": 52,\n",
            "      \"totalTokens\": 69\n",
            "    },\n",
            "    \"finish_reason\": \"tool_calls\"\n",
            "  },\n",
            "  \"tool_calls\": [\n",
            "    {\n",
            "      \"name\": \"pet_info_retriever\",\n",
            "      \"args\": {\n",
            "        \"input\": \"dogs\"\n",
            "      },\n",
            "      \"type\": \"tool_call\",\n",
            "      \"id\": \"call_n30LPDbegmytrj5GdUxZt9xn\"\n",
            "    }\n",
            "  ],\n",
            "  \"invalid_tool_calls\": [],\n",
            "  \"usage_metadata\": {\n",
            "    \"input_tokens\": 52,\n",
            "    \"output_tokens\": 17,\n",
            "    \"total_tokens\": 69\n",
            "  }\n",
            "}\n",
            "----\n",
            "TOOLS: ToolMessage {\n",
            "  \"content\": \"[{\\\"pageContent\\\":\\\"Dogs are great companions, known for their loyalty and friendliness.\\\",\\\"metadata\\\":{}}]\",\n",
            "  \"name\": \"pet_info_retriever\",\n",
            "  \"additional_kwargs\": {},\n",
            "  \"response_metadata\": {},\n",
            "  \"tool_call_id\": \"call_n30LPDbegmytrj5GdUxZt9xn\"\n",
            "}\n",
            "----\n",
            "AGENT: AIMessage {\n",
            "  \"id\": \"chatcmpl-9m9RJ3TT3ITfv6R0Tb7pcrNOUtnm8\",\n",
            "  \"content\": \"Dogs are known for being great companions, known for their loyalty and friendliness.\",\n",
            "  \"additional_kwargs\": {},\n",
            "  \"response_metadata\": {\n",
            "    \"tokenUsage\": {\n",
            "      \"completionTokens\": 18,\n",
            "      \"promptTokens\": 104,\n",
            "      \"totalTokens\": 122\n",
            "    },\n",
            "    \"finish_reason\": \"stop\"\n",
            "  },\n",
            "  \"tool_calls\": [],\n",
            "  \"invalid_tool_calls\": [],\n",
            "  \"usage_metadata\": {\n",
            "    \"input_tokens\": 104,\n",
            "    \"output_tokens\": 18,\n",
            "    \"total_tokens\": 122\n",
            "  }\n",
            "}\n",
            "----\n"
          ]
        }
      ],
      "source": [
        "import { createReactAgent } from \"@langchain/langgraph/prebuilt\";\n",
        "\n",
        "const tools = [\n",
        "  retriever.asTool({\n",
        "    name: \"pet_info_retriever\",\n",
        "    description: \"Get information about pets.\",\n",
        "    schema: z.string(),\n",
        "  })\n",
        "];\n",
        "\n",
        "const agent = createReactAgent({ llm: llm, tools });\n",
        "\n",
        "const stream = await agent.stream({\"messages\": [[\"human\", \"What are dogs known for?\"]]});\n",
        "\n",
        "for await (const chunk of stream) {\n",
        "  // Log output from the agent or tools node\n",
        "  if (chunk.agent) {\n",
        "    console.log(\"AGENT:\", chunk.agent.messages[0]);\n",
        "  } else if (chunk.tools) {\n",
        "    console.log(\"TOOLS:\", chunk.tools.messages[0]);\n",
        "  }\n",
        "  console.log(\"----\");\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "96f2ac9c-36f4-4b7a-ae33-f517734c86aa",
      "metadata": {},
      "source": [
        "This [LangSmith trace](https://smith.langchain.com/public/5e141617-ae82-44af-8fe0-b64dbd007826/r) shows what's going on under the hood for the above run.\n",
        "\n",
        "Going further, we can even create a tool from a full [RAG chain](/docs/tutorials/rag/):"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "bea518c9-c711-47c2-b8cc-dbd102f71f09",
      "metadata": {},
      "outputs": [],
      "source": [
        "import { StringOutputParser } from \"@langchain/core/output_parsers\";\n",
        "import { ChatPromptTemplate } from \"@langchain/core/prompts\";\n",
        "import { RunnableSequence } from \"@langchain/core/runnables\";\n",
        "\n",
        "const SYSTEM_TEMPLATE = `\n",
        "You are an assistant for question-answering tasks.\n",
        "Use the below context to answer the question. If\n",
        "you don't know the answer, say you don't know.\n",
        "Use three sentences maximum and keep the answer\n",
        "concise.\n",
        "\n",
        "Answer in the style of {answer_style}.\n",
        "\n",
        "Context: {context}`;\n",
        "\n",
        "const prompt = ChatPromptTemplate.fromMessages([\n",
        "  [\"system\", SYSTEM_TEMPLATE],\n",
        "  [\"human\", \"{question}\"],\n",
        "]);\n",
        "\n",
        "const ragChain = RunnableSequence.from([\n",
        "  {\n",
        "    context: (input, config) => retriever.invoke(input.question, config),\n",
        "    question: (input) => input.question,\n",
        "    answer_style: (input) => input.answer_style,\n",
        "  },\n",
        "  prompt,\n",
        "  llm,\n",
        "  new StringOutputParser(),\n",
        "]);"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4570615b-8f96-4d97-ae01-1c08b14be584",
      "metadata": {},
      "source": [
        "Below we again invoke the agent. Note that the agent populates the required parameters in its `tool_calls`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "06409913-a2ad-400f-a202-7b8dd2ef483a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "AGENT: AIMessage {\n",
            "  \"id\": \"chatcmpl-9m9RKY2nAa8LeGoBiO7N1SR4nAoED\",\n",
            "  \"content\": \"\",\n",
            "  \"additional_kwargs\": {\n",
            "    \"tool_calls\": [\n",
            "      {\n",
            "        \"id\": \"call_ukzivO4jRn1XdDpuVTI6CvtU\",\n",
            "        \"type\": \"function\",\n",
            "        \"function\": \"[Object]\"\n",
            "      }\n",
            "    ]\n",
            "  },\n",
            "  \"response_metadata\": {\n",
            "    \"tokenUsage\": {\n",
            "      \"completionTokens\": 30,\n",
            "      \"promptTokens\": 63,\n",
            "      \"totalTokens\": 93\n",
            "    },\n",
            "    \"finish_reason\": \"tool_calls\"\n",
            "  },\n",
            "  \"tool_calls\": [\n",
            "    {\n",
            "      \"name\": \"pet_expert\",\n",
            "      \"args\": {\n",
            "        \"context\": \"pirate\",\n",
            "        \"question\": \"What are dogs known for?\",\n",
            "        \"answer_style\": \"short\"\n",
            "      },\n",
            "      \"type\": \"tool_call\",\n",
            "      \"id\": \"call_ukzivO4jRn1XdDpuVTI6CvtU\"\n",
            "    }\n",
            "  ],\n",
            "  \"invalid_tool_calls\": [],\n",
            "  \"usage_metadata\": {\n",
            "    \"input_tokens\": 63,\n",
            "    \"output_tokens\": 30,\n",
            "    \"total_tokens\": 93\n",
            "  }\n",
            "}\n",
            "----\n",
            "TOOLS: ToolMessage {\n",
            "  \"content\": \"Dogs are known for their loyalty, companionship, and ability to provide emotional support to their owners.\",\n",
            "  \"name\": \"pet_expert\",\n",
            "  \"additional_kwargs\": {},\n",
            "  \"response_metadata\": {},\n",
            "  \"tool_call_id\": \"call_ukzivO4jRn1XdDpuVTI6CvtU\"\n",
            "}\n",
            "----\n",
            "AGENT: AIMessage {\n",
            "  \"id\": \"chatcmpl-9m9RMwAEc14TTKtitq3CH2x9wpGik\",\n",
            "  \"content\": \"A pirate would say that dogs are known for their loyalty, companionship, and ability to provide emotional support to their owners.\",\n",
            "  \"additional_kwargs\": {},\n",
            "  \"response_metadata\": {\n",
            "    \"tokenUsage\": {\n",
            "      \"completionTokens\": 26,\n",
            "      \"promptTokens\": 123,\n",
            "      \"totalTokens\": 149\n",
            "    },\n",
            "    \"finish_reason\": \"stop\"\n",
            "  },\n",
            "  \"tool_calls\": [],\n",
            "  \"invalid_tool_calls\": [],\n",
            "  \"usage_metadata\": {\n",
            "    \"input_tokens\": 123,\n",
            "    \"output_tokens\": 26,\n",
            "    \"total_tokens\": 149\n",
            "  }\n",
            "}\n",
            "----\n"
          ]
        }
      ],
      "source": [
        "const ragTool = ragChain.asTool({\n",
        "  name: \"pet_expert\",\n",
        "  description: \"Get information about pets.\",\n",
        "  schema: z.object({\n",
        "    context: z.string(),\n",
        "    question: z.string(),\n",
        "    answer_style: z.string(),\n",
        "  }),\n",
        "});\n",
        "\n",
        "const agent = createReactAgent({ llm: llm, tools: [ragTool] });\n",
        "\n",
        "const stream = await agent.stream({\n",
        "  messages: [\n",
        "    [\"human\", \"What would a pirate say dogs are known for?\"]\n",
        "  ]\n",
        "});\n",
        "\n",
        "for await (const chunk of stream) {\n",
        "  // Log output from the agent or tools node\n",
        "  if (chunk.agent) {\n",
        "    console.log(\"AGENT:\", chunk.agent.messages[0]);\n",
        "  } else if (chunk.tools) {\n",
        "    console.log(\"TOOLS:\", chunk.tools.messages[0]);\n",
        "  }\n",
        "  console.log(\"----\");\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "96cc9bc3-e79e-49a8-9915-428ea225358b",
      "metadata": {},
      "source": [
        "See this [LangSmith trace](https://smith.langchain.com/public/147ae4e6-4dfb-4dd9-8ca0-5c5b954f08ac/r) for the above run to see what's going on internally.\n",
        "\n",
        "## Related\n",
        "\n",
        "- [How to: create custom tools](/docs/how_to/custom_tools)\n",
        "- [How to: pass tool results back to model](/docs/how_to/tool_results_pass_to_model/)\n",
        "- [How to: stream events from child runs within a custom tool](/docs/how_to/tool_stream_events)\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "TypeScript",
      "language": "typescript",
      "name": "tslab"
    },
    "language_info": {
      "codemirror_mode": {
        "mode": "typescript",
        "name": "javascript",
        "typescript": true
      },
      "file_extension": ".ts",
      "mimetype": "text/typescript",
      "name": "typescript",
      "version": "3.7.2"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
